package com.ruoyi.user.service;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.user.dao.UserDetailDao;
import com.ruoyi.user.domain.UserDetail;
import com.ruoyi.user.model.LoginInfo;
import com.ruoyi.user.model.RegisterType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author: houyong
 * @date: 2025年11月14日 14:38
 */
@Slf4j
@Service
public class UserDetailService {


    private static final String SESSION_ID_PREX = "session:";
    @Autowired
    private UserDetailDao userDetailDao;
    @Autowired
    private RedisService redisService;
    @Value("${session.valid.days:30}")
    private Long sessionValidDays;

    /**
     * 根据手机号码获取用户详细信息
     *
     * @param phone 用户手机号码
     * @return 用户详细信息对象，如果不存在则返回null
     */
    public UserDetail getUserDetailByPhone(String phone) {
        // 执行查询并判断是否存在
        return userDetailDao.getUserDetailByPhone(phone);
    }

    /**
     * 根据邮箱地址获取用户详细信息
     *
     * @param email 用户邮箱地址
     * @return 用户详细信息对象，如果未找到则返回null
     */
    public UserDetail getUserDetailByEmail(String email) {
        return userDetailDao.getUserDetailByEmail(email);
    }

    /**
     * 用户注册方法
     * 根据注册类型选择手机号注册或邮箱注册，并进行相应的重复性校验
     *
     * @param phone    手机号码，当type为手机号注册时使用
     * @param email    邮箱地址，当type为邮箱注册时使用
     * @param password 密码
     * @param type     注册类型，参考RegisterType枚举
     * @return AjaxResult 注册结果，成功时返回注册信息，失败时返回错误信息
     */
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult register(String phone, String email, String password, int type) {
        // 手机号注册流程
        if (type == RegisterType.PHONE.getCode()) {
            // 检查手机号是否已存在
            if (userDetailDao.getUserDetailByPhone(phone) != null) {
                return AjaxResult.error("手机号已存在");
            }
            return AjaxResult.success(registerByPhone(phone, password));
        } else {
            // 邮箱注册流程
            // 检查邮箱是否已存在
            if (userDetailDao.getUserDetailByEmail(email) != null) {
                return AjaxResult.error("邮箱已存在");
            }
            return AjaxResult.success(registerByEmail(email, password));
        }
    }

    /**
     * 通过手机号注册用户
     *
     * @param phone    手机号码
     * @param password 用户密码
     * @return 注册成功的用户详细信息
     */
    public UserDetail registerByPhone(String phone, String password) {
        // 对密码进行MD5加密
        byte[] md5 = getPasswordMd5Bytes(password);
        long t = System.currentTimeMillis();
        // 生成唯一用户ID
        String uid = UUID.randomUUID().toString().replaceAll("-", "");
        UserDetail userDetail = new UserDetail();
        userDetail.setUid(uid);
        userDetail.setPhone(phone);
        userDetail.setStatus(1);
        userDetail.setPortrait("");
        userDetail.setName("");
        userDetail.setMd5(md5);
        userDetail.setT(t);
        // 保存用户信息到数据库
        userDetailDao.save(userDetail);
        return userDetail;
    }

    /**
     * 通过邮箱注册用户
     *
     * @param email    用户邮箱地址
     * @param password 用户密码
     * @return 注册成功的用户详细信息对象
     */
    public UserDetail registerByEmail(String email, String password) {
        // 对密码进行MD5加密处理
        byte[] md5 = getPasswordMd5Bytes(password);
        // 获取当前时间戳
        long t = System.currentTimeMillis();
        // 生成唯一的用户ID
        String uid = UUID.randomUUID().toString().replaceAll("-", "");
        // 创建并初始化用户详细信息对象
        UserDetail userDetail = new UserDetail();
        userDetail.setEmail(email);
        userDetail.setUid(uid);
        userDetail.setEmail(email);
        userDetail.setStatus(1);
        userDetail.setPortrait("");
        userDetail.setName("");
        userDetail.setMd5(md5);
        userDetail.setT(t);
        // 保存用户信息到数据库
        userDetailDao.save(userDetail);
        return userDetail;
    }

    /**
     * 获取密码的MD5摘要字节数组
     *
     * @param password 待加密的密码字符串
     * @return 返回密码的MD5摘要字节数组，如果加密过程中出现异常则返回null
     */
    private byte[] getPasswordMd5Bytes(String password) {
        byte[] md5 = null;
        try {
            // 使用MD5算法对密码进行摘要计算
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(password.getBytes(StandardCharsets.UTF_8));
            md5 = md.digest();
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return md5;
    }

    /**
     * 用户登录接口
     *
     * @param info 登录信息对象，包含登录类型、账号、密码等信息
     * @return AjaxResult 登录结果，包含登录成功或失败的信息
     */
    public AjaxResult login(LoginInfo info) {
        Integer type = info.getType();
        // 参数校验
        if (type == null || (type != RegisterType.EMAIL.getCode() && type != RegisterType.PHONE.getCode())) {
            return AjaxResult.error("注册类型错误");
        }
        // 根据登录类型调用对应的登录方法
        if (type == RegisterType.PHONE.getCode()) {
            return loginByPhone(info);
        } else {
            return loginByEmail(info);
        }
    }

    /**
     * 通过手机号码进行登录验证
     *
     * @param info 登录信息对象，包含手机号和密码
     * @return AjaxResult 登录结果，成功时返回sessionId和用户信息，失败时返回错误信息
     */
    public AjaxResult loginByPhone(LoginInfo info) {
        String phone = info.getPhone();
        String pwd = info.getPassword();
        // 获取密码的MD5字节数组
        byte[] passwordMd5Bytes = getPasswordMd5Bytes(pwd);
        // 根据手机号获取用户详细信息
        UserDetail userDetailByPhone = getUserDetailByPhone(phone);
        if (userDetailByPhone == null) {
            return AjaxResult.error("用户不存在");
        }
        // 验证密码是否正确
        if (!Arrays.equals(passwordMd5Bytes, userDetailByPhone.getMd5())) {
            return AjaxResult.error("密码不正确");
        }
        // 构造登录成功后的返回数据
        Map<String, Object> resultMap = new HashMap<>();
        String sessionId = UUID.randomUUID().toString().replaceAll("-", "");
        resultMap.put("sessionId", sessionId);
        resultMap.put("user", userDetailByPhone);
        // 将用户信息存入Redis缓存
        redisService.setCacheObject(SESSION_ID_PREX + sessionId, JSON.toJSONString(userDetailByPhone), sessionValidDays, TimeUnit.DAYS);
        return AjaxResult.success(resultMap);

    }

    /**
     * 通过邮箱登录系统
     *
     * @param info 登录信息对象，包含邮箱和密码
     * @return AjaxResult 登录结果，成功时返回sessionId和用户信息，失败时返回错误信息
     */
    public AjaxResult loginByEmail(LoginInfo info) {
        String email = info.getEmail();
        String pwd = info.getPassword();
        byte[] passwordMd5Bytes = getPasswordMd5Bytes(pwd);
        UserDetail userDetailByEmail = getUserDetailByEmail(email);
        // 验证用户是否存在
        if (userDetailByEmail == null) {
            return AjaxResult.error("用户不存在");
        }
        // 验证密码是否正确
        if (!Arrays.equals(passwordMd5Bytes, userDetailByEmail.getMd5())) {
            return AjaxResult.error("密码不正确");
        }
        // 登录成功，生成会话信息
        Map<String, Object> resultMap = new HashMap<>();
        String sessionId = UUID.randomUUID().toString().replaceAll("-", "");
        resultMap.put("sessionId", sessionId);
        resultMap.put("user", userDetailByEmail);
        redisService.setCacheObject(SESSION_ID_PREX + sessionId, JSON.toJSONString(userDetailByEmail), sessionValidDays, TimeUnit.DAYS);
        return AjaxResult.success(resultMap);

    }

    /**
     * 根据用户ID获取用户详细信息
     *
     * @param uid 用户唯一标识符
     * @return 用户详细信息对象，如果未找到则返回null
     */
    public UserDetail getUserDetailByUid(String uid) {
        return userDetailDao.getUserDetailByUid(uid);
    }


    public UserDetail getUserDetail(String uid) {
        return userDetailDao.getUserDetailByUid(uid);
    }

    /**
     * 根据用户ID列表获取用户详细信息列表
     *
     * @param uidList 用户ID列表
     * @return 用户详细信息列表
     */
    public List<UserDetail> getUserList(List<String> uidList) {

        // 调用DAO层方法查询用户列表
        List<UserDetail> userList = userDetailDao.getUserList(uidList);
        return userList;
    }


    @Transactional(rollbackFor = Exception.class)
    public AjaxResult updatePassword(String uid, String oldPassword, String newPassword) {
        UserDetail user = userDetailDao.getUserDetailByUid(uid);
        if (user == null) {
            return AjaxResult.error("用户不存在");
        }
        // 验证旧密码是否正确
        byte[] encryptedOldPassword = getPasswordMd5Bytes(oldPassword);
        if (!Arrays.equals(encryptedOldPassword, user.getMd5())) {
            return AjaxResult.error("原密码错误");
        }

        // 更新密码
        byte[] encryptedNewPassword = getPasswordMd5Bytes(newPassword);
        user.setMd5(encryptedNewPassword);
        userDetailDao.updateUserInfo(user);
        return AjaxResult.success("密码修改成功");
    }

    @Transactional(rollbackFor = Exception.class)
    public AjaxResult resetPassword(String uid, String newPassword) {
        UserDetail user = userDetailDao.getUserDetailByUid(uid);
        if (user == null) {
            return AjaxResult.error("用户不存在");
        }

        // 更新密码
        byte[] encryptedNewPassword = getPasswordMd5Bytes(newPassword);
        user.setMd5(encryptedNewPassword);
        userDetailDao.updateUserInfo(user);
        return AjaxResult.success("密码重置成功");
    }

    @Transactional(rollbackFor = Exception.class)
    public AjaxResult updatePhone(String uid, String newPhone) {
        // 检查新手机号是否已被其他用户使用
        UserDetail existingUser = userDetailDao.getUserDetailByPhone(newPhone);
        if (existingUser != null && !existingUser.getUid().equals(uid)) {
            return AjaxResult.error("该手机号已被其他用户绑定");
        }

        // 更新手机号
        userDetailDao.updatePhone(uid, newPhone);
        return AjaxResult.success("手机号修改成功");
    }

    @Transactional(rollbackFor = Exception.class)
    public AjaxResult updateEmail(String uid, String newEmail) {
        // 检查新邮箱是否已被其他用户使用
        UserDetail existingUser = userDetailDao.getUserDetailByEmail(newEmail);
        if (existingUser != null && !existingUser.getUid().equals(uid)) {
            return AjaxResult.error("该邮箱已被其他用户绑定");
        }

        // 更新邮箱
        userDetailDao.updateEmail(uid, newEmail);
        return AjaxResult.success("邮箱修改成功");
    }

    @Transactional(rollbackFor = Exception.class)
    public AjaxResult updateUserInfo(UserDetail userDetail) {
        userDetailDao.updateUserInfo(userDetail);
        return AjaxResult.success("用户信息修改成功");
    }

    public AjaxResult logoutBySessionId(String sessionId) {

        String key = String.format("session:%s", sessionId);
        Object cacheObject = redisService.getCacheObject(key);
        if (cacheObject != null) {
            redisService.deleteObject(key);
        }

        return AjaxResult.success();

    }


    public UserDetail getUserDetailBySessionId(String sessionId) {
        String key = String.format("%s%s", SESSION_ID_PREX, sessionId);
        Object cacheObject = redisService.getCacheObject(key);
        if (cacheObject != null) {
            return JSON.parseObject(cacheObject.toString(), UserDetail.class);
        }
        return null;
    }


    public IPage<UserDetail> getPagingUserList(UserDetail userDetail, Map<String, Object> params, Integer pageNum, Integer pageSize) {
        // 设置分页参数
        IPage<UserDetail> page = new Page<>(pageNum, pageSize);
        // 构造查询条件
        LambdaQueryWrapper<UserDetail> wrapper = new LambdaQueryWrapper<>();

        // 手机号条件
        if (StringUtils.isNotBlank(userDetail.getPhone())) {
            wrapper.like(UserDetail::getPhone, userDetail.getPhone());
        }

        // 邮箱条件
        if (StringUtils.isNotBlank(userDetail.getEmail())) {
            wrapper.like(UserDetail::getEmail, userDetail.getEmail());
        }

        // 状态条件
        if (userDetail.getStatus() != null) {
            wrapper.eq(UserDetail::getStatus, userDetail.getStatus());
        }

        // 注册时间范围条件
        if (params.get("startTime") != null) {
            wrapper.ge(UserDetail::getT, params.get("startTime"));
        }
        if (params.get("endTime") != null) {
            wrapper.le(UserDetail::getT, params.get("endTime"));
        }

        // 按注册时间倒序排列
        wrapper.orderByDesc(UserDetail::getT);
        // 执行分页查询
        IPage<UserDetail> result = userDetailDao.page(page, wrapper);

        return result;
    }
}
